package com.xuecheng.auth.service;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.client.XcServiceList;
import com.xuecheng.framework.domain.ucenter.ext.AuthToken;
import com.xuecheng.framework.domain.ucenter.response.AuthCode;
import com.xuecheng.framework.exception.ExceptionCast;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Created by Aniymx on 2019/1/11 15:52.
 */
@Service
public class AuthService {
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Value("${auth.tokenValiditySeconds}")
    int tokenValiditySeconds;

    public AuthToken login(String username, String password, String clientId, String clientSecret) {
        AuthToken authToken = this.applyToken(username, password, clientId, clientSecret);
        //jti
        String access_token = authToken.getAccess_token();
        String content = JSON.toJSONString(authToken);
        //存到redis
        boolean b = this.saveToken(access_token, content, tokenValiditySeconds);
        if (!b) {
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_TOKEN_SAVEFAIL);
        }
        return authToken;
    }

    private boolean saveToken(String access_token, String content, long ttl) {
        String name = "user_token:" + access_token;
        stringRedisTemplate.boundValueOps(name).set(content, ttl, TimeUnit.SECONDS);
        Long expire = stringRedisTemplate.getExpire(name);
        return expire > 0;
    }

    //认证方法
    private AuthToken applyToken(String username, String password, String clientId, String clientSecret) {
        ServiceInstance choose = loadBalancerClient.choose(XcServiceList.XC_SERVICE_UCENTER_AUTH);
        if (choose == null) {
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_AUTHSERVER_NOTFOUND);
        }
        URI uri = choose.getUri();
        //获取令牌的url
        String path = uri.toString() + "/auth/oauth/token";
        //定义body
        LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "password");
        body.add("username", username);
        body.add("password", password);
        //定义头
        LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        String authorization = this.httpbasic(clientId, clientSecret);
        header.add("Authorization", authorization);
        //指定 restTemplate当遇到400或者401不抛出异常,而是正常返回

        Map map = null;
        try {
            ((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() {
                @Override
                public void handleError(ClientHttpResponse response) throws IOException {
                    // 设置 当响应400和401时照常响应数据，不要报错
                    if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {
                        super.handleError(response);
                    }
                }
            });
            //http请求spring security的申请令牌接口
            ResponseEntity<Map> responseEntity = restTemplate.exchange(path, HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(body, header), Map.class);
            map = responseEntity.getBody();

        } catch (Exception e) {
            //e.printStackTrace();
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }
        if (map == null || map.get("access_token") == null || map.get("refresh_token") == null || map.get("jti") == null) {
            String error_description = (String) map.get("error_description");
            if (StringUtils.isNotEmpty(error_description)) {
                if (error_description.equals("坏的凭证")) {
                    ExceptionCast.cast(AuthCode.AUTH_CREDENTIAL_ERROR);
                } else if (error_description.indexOf("UserDetailsService returned null") >= 0) {
                    ExceptionCast.cast(AuthCode.AUTH_ACCOUNT_NOTEXISTS);
                }
            }
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }

        //取出授权码
        String access_token = (String) map.get("access_token");
        String refresh_token = (String) map.get("refresh_token");
        String jti = (String) map.get("jti");
        AuthToken authToken = new AuthToken();
        authToken.setAccess_token(jti);
        authToken.setRefresh_token(refresh_token);
        authToken.setJwt_token(access_token);
        return authToken;
    }

    private String httpbasic(String clientId, String clientSecret) {
        //将客户端id与密码进行拼接
        String str = clientId + ":" + clientSecret;
        byte[] encode = Base64Utils.encode(str.getBytes());
        return "Basic " + new String(encode);
    }


    /*从redis中进行查询*/
    public AuthToken getAuthToken(String token) {
        String userToken = "user_token:" + token;
        String jwt_token = stringRedisTemplate.opsForValue().get(userToken);
        if (jwt_token != null) {
            AuthToken authToken = null;
            try {
                authToken = JSON.parseObject(jwt_token, AuthToken.class);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return authToken;
        }
        return null;
    }
/*从redis进行删除*/
    public boolean  delToken(String token) {
        String name ="user_token:"+token;
        stringRedisTemplate.delete(name);
        return true;
    }

}
